簡單列一下一些Table的重點:
anyfunc
一種anyfunc
是在WebAssembly中定義的函數在Table區段宣告之後,在Element區段把要塞入Table的函數放進去就可以。像這樣:
(module
(table (export "table") 2 2 anyfunc)
(func $getone (result i32)
i32.const 1
)
(func $gettwo (result i32)
i32.const 2
)
(elem (i32.const 0) $getone $gettwo)
)
輸出的Table,在Javascript中,可以透過索引來取得並執行:
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script src="../wasm_util.js"></script>
<script>
let s = new Wasm('test007.wasm').getInstance();
s.then(instance => {
console.log(instance.exports.table.get(0)());
console.log(instance.exports.table.get(1)());
});
</script>
</body>
</html>
上面這段程式,透過WebAssembly程式輸出的Table索引0,就可以在Javascript環境呼叫$getone函數,同理可以透過索引1呼叫$gettwo函數。
執行的結果:
Table方便的地方,在於提供一個不需要import,也可以呼叫位於其他WebAssembly模組中的函數的方法。(但是需要知道函數的特徵,也就是參數的型別及返回值型別)類似動態連結。
要測試的話,會需要同時使用兩個WebAssembly模組,不過測試時發現之前寫的載入WebAssembly的程式有問題,所以改了一下:
(function(global) {
global.Wasm = Wasm;
function Wasm(_url) {
let module = null;
let url = _url;
this.getModule = getModule;
this.getInstance = getInstance;
function getModule() {
if(module === null) {
return new Promise((resolve, reject) => {
WebAssembly.compileStreaming(fetch(url))
.then(_module => {
module = _module;
resolve(_module)
})
.catch(reason => reject(reason))
});
} else {
return new Promise((resolve, reject) => resolve(module));
}
};
function getInstance(importObjects) {
if(module === null) {
return new Promise((resolve, reject) => {
getModule()
.then(_module => {
if(!!importObjects) resolve(new WebAssembly.Instance(_module, importObjects));
else resolve(new WebAssembly.Instance(_module));
})
.catch(reason => reject(reason))
});
} else {
return new Promise((resolve, reject) => {
if(!!importObjects) resolve(new WebAssembly.Instance(module, importObjects));
else resolve(new WebAssembly.Instance(module));
});
}
}
}
})(window);
然後就可以開始做簡單的測試。這次Table是在Javascript端產生,並且由兩個WebAssembly模組共享,第一個模組把函數透過Element區段放進Table,第二個模組則在輸出的函數中呼叫位於Table中的函數。先來看看網頁端:
<html>
<body>
<script src="../wasm_util.js"></script>
<script>
let importObjects = {
js: {
tbl: new WebAssembly.Table({initial: 2, element: 'anyfunc'})
}
};
new Wasm('test008a.wasm')
.getInstance(importObjects)
.then(instance => new Wasm('test008b.wasm').getInstance(importObjects))
.then(instance => {
console.log(instance.exports.getone());
console.log(instance.exports.gettwo());
});
</script>
</body>
</html>
因為要讓模組a先寫入Table,然後讓模組b呼叫,所以先後順序是很重要的。
第一個WebAssembly模組(test008a):
(module
(table (import "js" "tbl") 2 anyfunc)
(func $getone (result i32)
i32.const 1
)
(func $gettwo (result i32)
i32.const 2
)
(elem (i32.const 0) $getone $gettwo)
)
要呼叫Table中的函數,需要知道他的特徵。在這裡會使用Type區段來宣告呼叫的函數特徵。另外,呼叫Table中的函數時,會使用call_indirect
函數,他需要兩個參數,一個是要呼叫函數的索引,另外一個是特徵:
(test008b)
(module
(import "js" "tbl" (table 2 anyfunc))
(type $remote (func (result i32)))
(func (export "getone") (result i32)
i32.const 0
call_indirect (type $remote)
)
(func (export "gettwo") (result i32)
i32.const 1
call_indirect (type $remote)
)
)
在Javascript環境呼叫模組b輸出的getone函數,就會透過Table呼叫在模組a中定義的函數,gettwo也一樣。執行結果:
除了透過Memory說Hello的例子,目前練習的都只是基本的區塊使用方式。接下來就要更進一步來看比較複雜的程式流程,所以會開始使用到更多流程控制的指令,就從明天開始吧。